"""
格式：
| type（4byte）| len（4byte）| data（）| 三部分
len为data的长度，用16进制表示
为了data兼容中文，data使用utf-8编码，每个字符最长为4byte
所以data中字符最最多为 ( int(FFFF, 转10进制) == 65535  65535/4 == 16383 )
取15000个字符

为了消除发送缓存区与接收缓存区的影响，使用TextPtcAssembler进行协议对象装配

注意：对图像数据不进行utf-8编码，直接存储byte数据
"""
from queue import Queue

class TextProtocol:
    def __init__(self, txtype="", data=""):
        if txtype == "" and data == "":
            self.m_type = txtype
            self.m_data = data
        else:
            self.m_type = txtype.strip().ljust(4, ' ')
            if type(data) == type(b""):
                self.m_data = data[0:65000]
            else:
                self.m_data = data[0:15000]

    def type(self):
        return self.m_type.strip()

    def lenght(self):
        return self.m_data.__len__()

    def data(self):
        return self.m_data

    def serialize(self):
        ret = b''
        dba = b''
        if type(self.m_data) == type(b""):
            dba = self.m_data
        else:
            dba += self.m_data.encode(encoding="utf-8")
        ret += self.m_type.ljust(4, ' ').encode(encoding="utf-8")
        ret += str(hex(dba.__len__()))[2:].ljust(4, ' ').encode(encoding="utf-8")
        ret += dba

        return ret

    def unserialize(self, byte_data):
        ret = (byte_data.__len__() >= 8)
        ret = ret & (type(byte_data) == type(b''))

        if ret:
            self.m_type = str(byte_data[0:4], encoding="utf-8").strip()
            length = str(byte_data[4:8], encoding="utf-8").strip()
            length = int(length, 16)
            ret = ret & ((byte_data.__len__() - 8) == length)

            if ret:
                if self.m_type() == "IMG":
                    self.m_type = byte_data[8:]
                else:
                    self.m_data = str(byte_data[8:], encoding="utf-8")

        return ret


class TextPtcAssembler:
    def __init__(self):
        self.m_type = ""
        self.m_length = 0
        self.m_data = b''
        self.m_queue = Queue()

    def clear(self):
        self.m_type = ""
        self.m_length = 0
        self.m_data = b''

    def reset(self):
        self.clear()
        self.m_queue.queue.clear()

    def prepare(self, byte_data, length):
        for each in range(length):
            self.m_queue.put(byte_data[each:each+1])

    def fetch(self, length):
        ret = b''

        for each in range(length):
            ret += self.m_queue.get()

        return ret

    def assembler(self):
        ret = None

        if self.m_type == "":
            tryMseesge = self.makeTypeAndLength()
        else:
            tryMseesge = True

        if tryMseesge:
            ret = self.makeMessage()

        if ret is not None:
            self.clear()

        return ret

    def makeTypeAndLength(self):
        ret = False

        if self.m_queue.qsize() >= 8:
            self.m_type = str(self.fetch(4), encoding="utf-8")
            length = str(self.fetch(4), encoding="utf-8")
            try:
                self.m_length = int(length, 16)
                ret = True
            except:
                ret = False

            if ret is False:
                self.clear()

        return ret

    def makeMessage(self):
        ret = None
        need = self.m_length - self.m_data.__len__()

        if self.m_queue.qsize() < need:
            n = self.m_queue.qsize()
        else:
            n = need

        self.m_data += self.fetch(n)

        if self.m_length == self.m_data.__len__():
            if self.m_type.strip() == "IMG":
                ret = TextProtocol(self.m_type, self.m_data)
            else:
                ret = TextProtocol(self.m_type, str(self.m_data, encoding="utf-8"))

        return ret

def main():
    test = TextProtocol("Tex", "测试data")
    print("type:%s" % test.type())
    print("data:%s" % test.data())
    print("len:%x" % "测试data".encode(encoding="utf-8").__len__())
    da = test.serialize()

    ass = TextPtcAssembler()
    ass.prepare(da[0:7], da[0:7].__len__())

    test2 = ass.assembler()

    if test2 is not None:
        print("type2:%s" % test2.type())
        print("data2:%s" % test2.data())

    ass.prepare(da[7:], da[7:].__len__())

    test3 = ass.assembler()

    if test3 is not None:
        print("type3:%s" % test3.type())
        print("data3:%s" % test3.data())

if __name__ == "__main__":
    main()
